import def from "../utils/index.js";
import { arrayMethods } from "./array.js";
import Dep from "./dep.js";

export function observe(data) {
  if (typeof data !== "object" || data === null) return;
  // 如果存在属性劫持，则直接返回该实例
  if (data.__ob__ instanceof Observer) return data.__ob__;
  return new Observer(data);
}

class Observer {
  constructor(data) {
    this.dep = new Dep();
    def(data, "__ob__", this);
    if (Array.isArray(data)) {
      this.observeArray(data);
      this.protoAugment(data, arrayMethods);
    } else {
      this.walk(data);
    }
  }
  walk(data) {
    Object.keys(data).forEach((key) => defineReactive(data, key, data[key]));
  }

  observeArray(data) {
    for (let i = 0; i < data.length; i++) {
      observe(data[i]);
    }
  }

  protoAugment(data, arrayMethods) {
    data.__proto__ = arrayMethods();
  }
}

function dependArray(value) {
  for (let i = 0; i < value.length; i++) {
    let currentItem = value[i];
    currentItem && currentItem.__ob__ && currentItem.__ob__.dep.depend();
    if (Array.isArray(currentItem)) {
      dependArray(currentItem);
    }
  }
}

function defineReactive(target, key, value) {
  const childOb = observe(value);
  let dep = new Dep(); // 依赖收集
  Object.defineProperty(target, key, {
    get() {
      if (Dep.target) {
        dep.depend();
        if (childOb) {
          childOb.dep.depend();
          if (Array.isArray(value)) {
            dependArray(value);
          }
        }
      }
      return value;
    },
    set(newValue) {
      if (newValue === value) return;
      value = newValue;
      dep.notify();
    },
  });
}
